package com.zhuangyuan.cloud.data.service;

import com.zhuangyuan.cloud.data.entities.BaseEntity;
import com.zhuangyuan.cloud.data.entities.CateCategory;
import com.zhuangyuan.cloud.data.entities.Category;
import com.zhuangyuan.cloud.data.repository.sqlite.CategoryRepository;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.PropertyAccessor;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManagerFactory;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import java.util.*;

@Service
public class CategoryServiceImpl extends ABaseService implements CategoryService{
    @Autowired
    private CategoryRepository categoryRepository;
    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @Override
    public List<Category> list() {
        List<Category> categories = categoryRepository.findAll();
        List<Category> parents = new ArrayList<>();
        Iterator<Category> iterator = categories.iterator();
        while (iterator.hasNext()){
            Category p = iterator.next();
            if(p.getLevel() == 0){
                parents.add(p);
            }
        }
        parents.forEach(p->{
            categories.forEach(c->{
                if(c.getLevel() > 0 && p.getId().equals(c.getParentId())){
                    p.addChild(c);
                }
            });
        });

        return parents;
    }

    @Override
    public List<Category> list(int level) {
        return categoryRepository.findByLevel(level);
    }

    @Override
    public List<Category> children(String id) {
        return categoryRepository.children(id);
    }

    @Override
    public Category add(Category c) {
        List<Category> list = categoryRepository.findByName(c.getName());
        if(list.size() == 0) {
            categoryRepository.save(c);
            return c;
        }else{
            return list.get(0);
        }
    }

    private boolean checkExists(Category c){
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("name", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT)).withIgnoreCase();
        Example<Category> exa = Example.of(c,matcher);
        return categoryRepository.exists(exa);
    }

    @Override
    public List<Category> addAll(List<Category> categories) {
        List<Category> list = new ArrayList<>();
        categories.forEach(m->{
            list.add(add(m));
        });
        return list;
    }

    @Override
    public boolean delete(String id) throws Exception {
        boolean hasChild = hasChild(id);

        if(hasChild){
            throw new Exception("Can not delete specified category with id "+id);
        }

        SessionFactory sf = entityManagerFactory.unwrap(SessionFactory.class);
        Session session = sf.openSession();
        Query query = session.createQuery("select cc from CateCategory cc where cc.category.id = ?categoryId");
        query.setParameter("categoryId",id);
        int referencedCount = (int)query.getSingleResult();

        if(referencedCount > 0){
            throw new Exception("Can not delete specified category because it referenced by Cate.");
        }

        if(!hasChild){
            delete(get(id));
        }
        return true;
    }


    @Override
    public boolean exists(Map<String, Object> propertyValue) throws Exception {
        ExampleMatcher matcher = ExampleMatcher.matching();
        ExampleMatcher.GenericPropertyMatcher defaultMatcher =  ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT);
        Iterator<String> iterator = propertyValue.keySet().iterator();
        Category c = new Category();
        PropertyAccessor pa = PropertyAccessorFactory.forBeanPropertyAccess(c);
        while (iterator.hasNext()){
            String prop = iterator.next();
            Object value = propertyValue.get(prop);
            matcher = matcher.withMatcher(prop,defaultMatcher);
            pa.setPropertyValue(prop,value);
        }
        matcher = matcher.withIgnoreCase();
        Example<Category> example = Example.of(c,matcher);
        return categoryRepository.exists(example);
    }


    @Override
    public boolean hasChild(String id) {
        return categoryRepository.childrenCount(id)>0;
    }

    @Override
    public boolean canDelete(String id) {
        return false;
    }

    @Override
    protected JpaRepository getRepository() {
        return this.categoryRepository;
    }
}
